/** \file
 * @brief MSP430/GCC port of the RTOS
 * 
 * @page msp430gcc_port  HAL for MSP430 family with  mspgcc compiler
 * \section msp430gcc_port_section  HAL for MSP430 family with  mspgcc compiler
 * This is the TiROS hardware abstraction layer for the Texas
 * Instruments MSP430x family of embedded microcontrollers. This
 * microcontroller family can support software interrupts. These
 * microcontrollers can be driven from a 32kHz crystal oscillator.
 * The time functions have been written assuming such an oscillator
 * drives the timer. If this is not the case, modify the functions in porttime.h
 * and the timer initialization functions in tr_port.c.
 *
 * Note that in the MSP430 architecture, the low-power state of the
 * processor is stored in the status register.  Since this is part of
 * the context, per-task power control is automatic.  To keep the
 * processor in low-power state during idle time, simple define
 * the idle task as
 * \code
 * void idle_task(void *dummy) 
 * {
 *   while(1) {
 *       _BIS_SR(LPM3_bits);
 *   }
 * }
 * \endcode
 * 
 * Tested with TI MSP430x1611
 * <BR>
 * <H2> Configuration Options: </H2>
 * <DL>
 *    <DT>  TRPORT_MSP_OSTIMER [A | B] : <DD>Options are A or B.  The MSP430 family
 * supports two timers, TimerA and TimerB.  TimerA is available on the
 * entire family line. TimerB is only available on some.  By default,
 * if this configuration option is not set, TimerA is used as source
 * for the timer interrupt.
 * </DL>
 * <H2> Writing ISRs. </H2>
 *
 *   If no TiROS API functions will be called directly or indirectly
 *  by , interrupts will stay
 *  disabled, then the ISR can be
 *  written very simply. See the requirements in \ref isr.
 * \code
 * interrupt (IVECTOR) ISR_function(void)
 * {
 *  // do stuff
 * }
 * \endcode
 *
 * If TiROS API calls have to be used,  then it is used as follows
 * \code
 *
 * #include "tr_int.h"
 * interrupt (IVECTOR) ISR_function(void) __attribute__ ( (naked))
 * interrupt (IVECTOR) ISR_function(void) 
 * {
 *    HALINT_ISR_ENTRY();
 *    int x;
 *    OS_CRITICAL_ENABLE();   // Only needed if any critical sections
 *                           // will be used and interrupt nesting is enabled.
 *
 *   OS_ISR_BEGIN();         // Mark the beginning of an ISR. Note
 *                           // that if OS_CRITICAL_ENABLE() is also
 *                           // present, then this comes after 
 *                           // that. 
 *  
 *  x = 3;
 *    Do more stuff ....
 *  
 *  OS_ISR_END();
 *  HALINT_ISR_EXIT();
 * }
 * \endcode
 * 
 *
 *
 * <H2> Other Notes </H2>
 * This port also allows the usage of an alternate stack for interrupt
 * handling.  These alternate stack handling is used with the
 * functions OS_PORT_BEGIN_ALTSTK() and OS_PORT_END_ALTSTK()
 * <BR>
 * Software traps from within an ISR are possible but are
 * not used in this port since a subtle race condition in the
 * interrupt handler can cause problems.  i.e., after return from an
 * ISR where a software trap has been invoked, the invoked trap
 * interrupt is not always run immediately.
 *
 */

/* Author: Ratish J. Punnoose, 2006
 * This file is part of TiROS, the Tickless Real-Time Operating System.
 * Copyright(c) 2006, 2007: Ratish J. Punnoose.
 * Copyright(c) 2006 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
 * certain rights in this software. 
 * 
 * TiROS is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 or (at your option) any later version.
 *
 * TiROS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with TiROS; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * As a special exception, if other files instantiate templates or use macros
 * or inline functions from this file, or you compile this file and link it
 * with other works to produce a work based on this file, this file does not
 * by itself cause the resulting work to be covered by the GNU General Public
 * License. However the source code for this file must still be made available
 * in accordance with section (3) of the GNU General Public License.
 *
 * This exception does not invalidate any other reasons why a work based on
 * this file might be covered by the GNU General Public License.
 *
 */


#ifndef __MSP430GCC_TIROS_PORT_H
#define __MSP430GCC_TIROS_PORT_H
/*@ignore@*/
#define TIROS_USER_CONFIG
#include "proj_config.h"
#undef TIROS_USER_CONFIG

/*@end@*/

#include <io.h>
#include <signal.h>
#include <stdint.h>

/* The data type used by this port for subtime_t */
#define subtime_t uint16_t
#define LT_INLINE static inline 



#include <tiros/tr_time.h>
/* Insert your port for time functions for your hardware */
#include "porttime.h"

/* Define the value of infinite time for this port */
#define TRPORT_INF_TIME {~0 , ~0 }



/* The msp port can use registers for passing return values to
    blocked tasks */
#define TIROS_REGISTER_PASSING



/* Define basic types for   the hardware word, boolean, and pointer */
typedef uintptr_t osword_t;
typedef void*     osptr_t;
typedef osword_t*  osstkptr_t;
typedef void (* osfnptr_t)(void *fnarg);
typedef uintptr_t  osptrword_t;

#define ILLEGAL_STACK ((osstkptr_t) ~0)
#define TR_ILLEGAL_ADDR  ((osptr_t)    ~0)



/* Port specific function implemented in tr_port.c */

/* \name Context_switching_functions
 * @{ */

/* Initialize the stack for a task.
 * @param stk  Pointer to the stack.
 * @param stacksize  Size of the stack.
 * @param PC   Program counter.
 * @param init_arg   Initial argument passed to the task */
/*@shared@*/
osstkptr_t hal_stk_init(osword_t *stk, osword_t stacksize, osfnptr_t pc,
			osptr_t init_arg); 


#ifdef TIROS_REGISTER_PASSING
/* Set a return value for a task whose context is stored.
 *
 * This injects a return value into the context of a task that is currently frozen.
 * @param stackptr  Pointer to the stack.  The stack should also contain the context.
 * @param retval    The return value to be injected into the task context */
static inline void hal_retval_set(osstkptr_t stackptr, 
				  /*@null@*/ osptr_t retval)
{
	/* GCC passes the return value in register 15, which is what
	 * stackptr points to */  
	*stackptr = (osword_t)retval;
}
#endif



void hal_init(void);


void hal_time_get(/*@out@*/ trtime_t *t);
int hal_time_set(const trtime_t *t);



/* Set an alarm.
 *  When the alarm is reached, osint_alarm_reached is called
 * @param lt Pointer to time */
void  hal_alarm_set(/*@null@*/ const trtime_t *lt);


/*lint -esym(438, osint_int_status) : Prevent lint messages about
 * osint_int_status not used */

/*lint -esym(530, osint_int_status) : Prevent lint messages about
 * osint_int_status not initialized */

/*lint -esym(550, osint_int_status) : Prevent lint messages about
 * osint_int_status not accessed */

/*lint -esym(578, osint_int_status) : Prevent lint messages about
 * osint_int_status being hidden */

/* Enable critical section calls in the the called function
 *
 *  This has to be called at the beginning of the function before
 *  calling OS_CRITICAL_BEGIN or OS_CRITICAL_END.  This should be the
 *  first call after variable declaration.  These section should not
 *  be added within an ISR if Interrupt nesting is turned on, because
 *  the interrupt nesting adds this in automatically, and duplication
 *  will result in compiler complaints. */
#define OS_PORT_CRITICAL_ENABLE()   /*@unused@*/  osword_t osint_int_status
#define OS_PORT_ISR_BEGIN()                     		\
	extern uint8_t os_isr_nesting;          		\
	os_isr_nesting++;


#ifndef S_SPLINT_S
/* Begin a critical section 
 * 
 * The current interrupt state is saved in osint_int_status and then
 * interrupts are disabled 
 *  According to MSP430 documentation Pg 3-39, 3-40, the instuction
 * following EINT is always executed even if an ISR is pending.
 * Similarly, if any code sequence should be protected from
 * interruption, the DINT should be executed at least one instruction
 * before the uninterruptible sequence, or be followed by a nop. 
 * The osint_int_status &= GIE, saves only the interrupt
 * state. Similarly, only the interrupt state is restored later.
 * In this case, we don't add a NOP in OS_PORT_CRITICAL_BEGIN since
 * dint is followed by a non critical instruction.
*/
#define OS_PORT_CRITICAL_BEGIN()  \
    asm volatile("mov   r2, %0  \n\t"  \
                 "dint          \n\t" :  "=r" (osint_int_status)  : ); \
		 osint_int_status &= GIE; 



/* End a critical section
 *
 * The saved interrupt state is restored */
#define OS_PORT_CRITICAL_END()  \
    asm volatile("bis  %0, r2   \n\t" \
                 "nop           \n\t" : : "r" (osint_int_status) );



/* Enable interrupts
 * This allows the user to explicitly enable interrupts */
#define OS_PORT_INT_ENABLE()      _EINT(); _NOP();

/* Disable interrupts
 * This allows the user to explicitly disable interrupts */
#define OS_PORT_INT_DISABLE()       _DINT(); _NOP();




/** Use an alternate stack within an interrupt function.
 * 
 * @param altstkptr  The pointer to an array to hold the new stack.
 * @param ALTSTKSZ  The size of the altnernate stack. 
 *
 * Implementation:
 * Step1: Save SP to top of new stack location.
 * Step2: Set  SP to top of new stack location.
 * Restrictions on USE:
 * This should not be followed by any push commands without an
 * associated pop.
 * When using printf within this section, the compiler may push the
 * arguments onto the stack (also if the number of arguments to a
 * function is more than the nunber of passing registers).  Once
 * pushed, the compiler may neglect to pop the stack for
 * optimization.  In this case, the stack  values will be haywire. 
 * To guarantee that everything works well, enclose the section
 * between OS_PORT_BEGIN_ALTSTK and OS_PORT_END_ALTSTK in a function.
 */
#define OS_PORT_BEGIN_ALTSTK(altstkptr, ALTSTKSZ) \
 ({   \
     const osstkptr_t __x = altstkptr + ALTSTKSZ-1; \
     asm volatile( \
           "mov r1, %0 \n\t"  \
           "mov %1, r1 \n\t"  \
           : "=m"  (*__x)     \
           : "i" (__x) );  \
 })





/** End operation on the alternate stack. */
#define OS_PORT_END_ALTSTK() \
    asm volatile("mov.w 0(r1), r1 ");

/* Context switch from user level (non-ISR)
 *  This function is called from within an OS call, to switch the
 *  running process.  It also provides a return value. This is a
 *  pseudo return-value.  This function does not actually determine
 *  the value returned.  The return value is set by hal_retval_set
 *  which is often invoked when waking the process up from a blocked
 *  state. 
 * In this mspgcc port, the hal_ctxt_switch has the naked attribute to
 * prevent gcc from adding miscellaneous header code to the
 * function. This happens when compiling without optimization.
 *    
 * @return  Return value.  */
osstkptr_t hal_ctxt_switch(void)  __attribute__ ( (naked) ) ;

/* Initially 14 words are used to store MSP430 register intialization,
 * R0 - R15 , without R1, R3*/
#define MSP430_INIT_STACK_OCCUPANCY      14

#define TRPORT_MIN_CTXT_SZ   (MSP430_INIT_STACK_OCCUPANCY + 1 )


/* Load the given context
 * @param ctxt_ptr   Pointer to task context */
static inline void hal_ctxt_load(osstkptr_t ctxt_ptr) 
{
	/* In this port, the stack pointer provides the contest */
	asm volatile (	
		"mov.w  %0, r1 \n\t"
		"pop	r15	\n\t"
		"pop	r14	\n\t"
		"pop	r13	\n\t"
		"pop	r12	\n\t"
		"pop	r11	\n\t"
		"pop	r10	\n\t"
		"pop	r9	\n\t"
		"pop	r8	\n\t"
		"pop	r7	\n\t"
		"pop	r6	\n\t"
		"pop	r5	\n\t"
		"pop	r4	\n\t"
		"reti		\n\t"
		: /* no output registers */
		: "r" (ctxt_ptr)
		);
} 



/* HAL Internal macro used to save the registers.
 * This can be called from an interrupt function. It does not save the
 * status register.  If being called from non-interrupt code, the
 * status register should be saved before calling this function */
#define HALINT_CTXT_SAVE() \
		"push   r4    \n\t"	\
		"push   r5    \n\t"	\
		"push   r6    \n\t"	\
		"push   r7    \n\t"	\
		"push   r8    \n\t"	\
		"push   r9    \n\t"	\
		"push   r10   \n\t"	\
		"push   r11   \n\t"	\
		"push   r12   \n\t"	\
		"push   r13   \n\t"	\
		"push   r14   \n\t"	\
		"push   r15   \n\t"	\
		"mov.w  r1, r15 \n\t"	



#endif /* Bypass SPLINT verification */

/* @} End context switching functions  */	







#ifndef TRPORT_RESPONSE_TIME
#define PORT_WAKEUP_RESPONSE_SUBUNITS  40
#define TRPORT_RESPONSE_TIME  {0, PORT_WAKEUP_RESPONSE_SUBUNITS }
#endif






#ifndef TRPORT_MSP_OSTIMER
/* Which timer on the MSP430 should be used for servicing the ticks.
 * For a default use TIMER A since it is available on all the MSP430x
 * family. */
#define TRPORT_MSP_OSTIMER  A
#endif




#ifdef TIROS_KERNEL_TRAP_ENABLED
#error "Deprecated directive TIROS_KERNEL_TRAP_ENABLED in msp430_gcc port"
#endif






extern int halint_kernel_trap_flagged;

/* Force a software interrupt */
static inline void OS_KERNEL_TRAP(void)  
{ halint_kernel_trap_flagged = 1;}

/*@shared@*/ osstkptr_t osint_taskswitcher(/*@shared@*/ osstkptr_t old_ctxtptr);

/*lint -esym(530, ctxt_ptr) : Prevent lint messages about
 * ctxt_ptr not being initialized */


/** Macro called upon entry to an ISR. This shields us from the actual
 * detail of the kernel trap implementaion */
#define HALINT_ISR_ENTRY()   		\
	register osstkptr_t ctxt_ptr;   	\
        asm volatile (  		\
                "push   r4    \n\t" 	\
                "push   r5    \n\t" 	\
                "push   r6    \n\t"	\
                "push   r7    \n\t"	\
                "push   r8    \n\t"	\
                "push   r9    \n\t"	\
                "push   r10   \n\t"	\
                "push   r11   \n\t"	\
                "push   r12   \n\t"	\
                "push   r13   \n\t"	\
                "push   r14   \n\t"	\
                "push   r15   \n\t"	\
                "mov.w  r1, %0 \n\t"	\
                : "=r" (ctxt_ptr) 	\
                : 			\
                );			\
	halint_kernel_trap_flagged = 0;

/** Macro called upon exit from an ISR. This shields us from the actual
 * detail of the kernel trap implementaion */
#define HALINT_ISR_EXIT()		\
	if (halint_kernel_trap_flagged)	\
		ctxt_ptr = osint_taskswitcher(ctxt_ptr);	\
	hal_ctxt_load(ctxt_ptr);









#ifdef TIROSINT_DATA_DEBUG
/* Compute the program counter, from the stored context (stack
 * pointer)
 * This function is used for debugging.  Implement this by extracting
 * the program counter out of the stack pointer if possible.
 *
 * @param ctxt_ptr  The context of the current task
 * @return The program counter */
osptrword_t hal_pc_from_ctxt(osstkptr_t ctxt_ptr);




#ifdef TIROS_STK_CHECK
/* Return the maximum stack occupancy of the task.
 * This hal implemented function should provide the 
 * maximum stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr        Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The maximum stack occupancy */
osword_t hal_stkusage_max(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			  osword_t stksize);

/* Return the current stack occupancy of the task.
 * This hal implemented function should provide the 
 * current stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr   Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The current stack occupancy */
osword_t hal_stkusage_curr(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			   osword_t stksize);
#endif /*TIROS_STK_CHECK */

#endif






/* End of tr_port.h  ======================================== */
#endif   
